package com.SmartCare.service.impl.medicine;

import com.SmartCare.mapper.homeInfo.CareHomeDetailsMapper;
import com.SmartCare.mapper.medicine.MedicationListMapper;
import com.SmartCare.mapper.medicine.MedicationMapper;
import com.SmartCare.mapper.medicine.MedicationRecordMapper;
import com.SmartCare.mapper.role.ElderlyInformationMapper;
import com.SmartCare.mapper.role.UserMapper;
import com.SmartCare.pojo.homeInfo.CareHomeDetails;
import com.SmartCare.pojo.medicine.MedicationRecord;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.SmartCare.common.enums.Constants;
import com.SmartCare.common.Result;
import com.SmartCare.common.exception.ServiceException;
import com.SmartCare.common.dto.medicine.ManagerMedicationListDTO;
import com.SmartCare.common.dto.medicine.MedicationListDTO;
import com.SmartCare.common.dto.medicine.MedicineDTO;
import com.SmartCare.service.medicine.MedicationListService;
import com.SmartCare.pojo.role.ElderlyInformation;
import com.SmartCare.pojo.medicine.MedicationList;
import com.SmartCare.utils.ElderTransToDTO;
import com.SmartCare.utils.MedicationListTransToDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;


@Service
public class MedicationListServiceImpl extends ServiceImpl<MedicationListMapper, MedicationList> implements MedicationListService {
    @Autowired
    private MedicationListMapper medicationListMapper;

    @Resource
    private ElderlyInformationMapper elderlyInformationMapper;

    @Resource
    private MedicationListTransToDTO medicationListTransToDTO;

    @Resource
    private UserMapper userMapper;

    @Resource
    private ElderTransToDTO elderTransToDTO;

    @Resource
    private MedicationMapper medicationMapper;

    @Resource
    private CareHomeDetailsMapper careHomeDetailsMapper;

    @Resource
    private MedicationRecordMapper medicationRecordMapper;

    @Override
    public Result medicineAdd(MedicineDTO medicineDTO, String listId) {
        long listNum = 0L;
        List<MedicationList> list = medicineDTO.getMedicineList();

        if (listId == null) {
            //第一条记录直接添加,生成要药单编号
            //使用雪花算法生成工具生成药单编号
            listNum = IdWorker.getId(list.get(0));
        }
        if (listId != null && !listId.isEmpty()) {
            listNum = Long.parseLong(listId);
        }

        for (int i = 0; i < list.size(); i++) {
            MedicationList medication = list.get(i);
            medication.setListNum(listNum);

            //根据药品的服用时间判断为长期还是临时
            long timeDifference = medication.getEndTime().getTime() - medication.getStartTime().getTime();
            medication.setTerm(timeDifference >= (24 * 60 * 60 * 1000 * 5));

            medicationListMapper.insert(medication);
        }
        return Result.success("添加记录成功");
    }

    /**
     * 获取同一个药单下第一条添加的数据的listNum
     *
     * @param medicationList
     * @return
     */
    public Long getListNum(MedicationList medicationList) {
        LambdaQueryWrapper<MedicationList> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MedicationList::getListNum, medicationList.getListNum());
        wrapper.eq(MedicationList::getMedicationId, medicationList.getMedicationId());
        wrapper.eq(MedicationList::getFrequencyId, medicationList.getFrequencyId());
        wrapper.eq(MedicationList::getDosage, medicationList.getDosage());
        wrapper.eq(MedicationList::getElderlyId, medicationList.getElderlyId());
        wrapper.eq(MedicationList::getNurseId, medicationList.getNurseId());
        wrapper.eq(MedicationList::getStartTime, medicationList.getStartTime());
        wrapper.eq(MedicationList::getEndTime, medicationList.getEndTime());
        MedicationList one = medicationListMapper.selectOne(wrapper);
        return one.getListNum();
    }


    @Override
    public Result getListsByElderId(Long elderId) {

        //先获取老人对象并判空
        ElderlyInformation one = elderlyInformationMapper.selectOne(new QueryWrapper<ElderlyInformation>().eq("elderly_id", elderId));
        if (one == null) {
            throw new ServiceException(Constants.CODE_400, "老人ID不存在");
        }

        //查询该护工负责的该老人所有的药单记录
        List<MedicationList> records = list(new QueryWrapper<MedicationList>().eq("elderly_id", elderId));

        //根据药单编号进行分组
        Map<Long, List<MedicationList>> recordsByListNum = records.stream()
                .collect(Collectors.groupingBy(MedicationList::getListNum));


        //逐个排除不在用药时间内的药品

        //结果返回Map，key为药单序号，value为dto数组
        Map<Long, List<MedicationListDTO>> result = new HashMap<>();
        //遍历根据药单序号分组后的map
        recordsByListNum.forEach((k, v) -> {
            List<MedicationListDTO> resultList = new ArrayList<>();
            //获取当前时间，方便后续进行比较
            Date current = new Date();
            //遍历value中的药单项序列
            for (MedicationList l : v) {
                //进行时间判断，如果在服用时间段就添加到resultList中
                if (l.getStartTime().before(current)) {
                    if (l.getEndTime().after(current)) {
                        //将medicationList对象转为dto返回
                        MedicationListDTO dto = medicationListTransToDTO.transToDTO(l);
                        //添加至list中
                        resultList.add(dto);
                    }
                }
            }
            //将list和药单序号分别作为value和key添加至map
            result.put(k, resultList);
        });

        //将修整后的药单记录返回
        return Result.success(result);
    }

    @Override
    public Result getAllById(Long elderlyId) {
        //先获取老人对象并判空
        ElderlyInformation one = elderlyInformationMapper.selectOne(new QueryWrapper<ElderlyInformation>().eq("elderly_id", elderlyId));
        if (one == null) {
            throw new ServiceException(Constants.CODE_400, "老人ID不存在");
        }
        //获取记录
        List<MedicationList> records = list(new QueryWrapper<MedicationList>().eq("elderly_id", elderlyId));
        //按照药单编号分组
        Map<Long, List<MedicationList>> recordsByListNum = records.stream()
                .collect(Collectors.groupingBy(MedicationList::getListNum));
        //定义返回结果
        Map<Long, List<MedicationListDTO>> result = new HashMap<>();
        //遍历map然后遍历map的value即药单项list，将每个药单项转换为dto
        recordsByListNum.forEach((k, v) -> {
            List<MedicationListDTO> resultList = new ArrayList<>();
            //遍历value中的药单项序列
            for (MedicationList l : v) {
                //将medicationList对象转为dto返回
                MedicationListDTO dto = medicationListTransToDTO.transToDTO(l);
                //添加至list中
                resultList.add(dto);
            }
            //将list和药单序号分别作为value和key添加至map
            result.put(k, resultList);
        });
        return Result.success(result);
    }

    @Override
    public Result getAll(String careHomeName, String keyword) {
        //获取养老院信息
        CareHomeDetails careHome = careHomeDetailsMapper.selectOne(new QueryWrapper<CareHomeDetails>().eq("name", careHomeName));
        if (careHome == null) {
            return Result.error("养老院信息不存在");
        }
        //获取该养老院的老人列表
        QueryWrapper<ElderlyInformation> elderQw = new QueryWrapper<ElderlyInformation>();
        elderQw.eq("current_care_home_id", careHome.getId());
        elderQw.like("elderly_name", keyword);
        List<ElderlyInformation> properElders = elderlyInformationMapper.selectList(elderQw);
        //获取他们的id数组
        List<Long> elderIds = properElders.stream().map(ElderlyInformation::getElderlyId).collect(Collectors.toList());
        //判空
        if (elderIds.size() == 0) {
            return Result.success(new ArrayList<>());
        }

        //获取符合条件的药单项集合
        QueryWrapper<MedicationList> qw = new QueryWrapper<>();
        qw.in("elderly_id", elderIds);
        List<MedicationList> list = list(qw);

        //根据药单编号进行分组
        Map<Long, List<MedicationList>> groupByListNum = list.stream()
                .collect(Collectors.groupingBy(MedicationList::getListNum));

        //定义结果：ManagerMedicationListDTO的集合
        List<ManagerMedicationListDTO> result = new ArrayList<>();

        //遍历map生成ManagerMedicationListDTO对象
        groupByListNum.forEach((listNum, recordList) -> {
            ManagerMedicationListDTO dto = new ManagerMedicationListDTO();
            //设置药单编号
            dto.setListNum(listNum);
            //获取老人和护工信息
            if (recordList.get(0).getElderlyId() != null) {
                dto.setElder(elderTransToDTO.trans(elderlyInformationMapper.selectById(recordList.get(0).getElderlyId())));
            }
            if (recordList.get(0).getNurseId() != null) {
                dto.setNurse(userMapper.selectById(recordList.get(0).getNurseId()));
            }
            //设置药单项
            List<MedicationListDTO> medicationListDTOS = new ArrayList<>();
            for (MedicationList m : recordList) {
                if (medicationMapper.selectById(m.getMedicationId()) == null) {
                    //如果药品信息不存在则删除此条记录
                    removeById(m.getId());
                    continue;
                }
                MedicationListDTO mlDTO = medicationListTransToDTO.transToDTO(m);
                medicationListDTOS.add(mlDTO);
            }
            medicationListDTOS.sort(Comparator.comparing(MedicationListDTO::getStartTime));

            dto.setList(medicationListDTOS);
            result.add(dto);
        });
        return Result.success(result);
    }

    @Override
    public Result addOption(MedicationList medicationList) {

        //根据药品的服用时间判断为长期还是临时
        long timeDifference = medicationList.getEndTime().getTime() - medicationList.getStartTime().getTime();
        boolean term = timeDifference >= (24 * 60 * 60 * 1000 * 5);

        /*
          检测该老人的其他药单：
          如果有两个及以上的药单，直接返回错误；
          如果有一个药单，判断是长期还是短期，跟现在要创建的药单进行对比
          如果现在创建的药单和已存在药单的term相同，返回错误；否则正常创建
         */

        if (medicationList.getListNum() == null) {
            //如果是新创建的药单，获取老人的其他药单
            List<MedicationList> lists = list(new QueryWrapper<MedicationList>().eq("elderly_id", medicationList.getElderlyId()));
            Map<Long, List<MedicationList>> map = lists.stream().collect(Collectors.groupingBy(MedicationList::getListNum));
            if (map.size() >= 2) {
                return Result.error(Constants.CODE_400, "该老人已有长期短期两个药单");
            }
            if (map.size() == 1) {
                //获取已存在药单的类型
                boolean existTerm = lists.get(0).getTerm();
                //与当前要创建的类型相同则报错
                if (term == existTerm) {
                    String msg = "该老人已有" + (existTerm ? "长期" : "短期") + "药单, 无法重复创建";
                    return Result.error(Constants.CODE_400, msg);
                }
            }
        } else {
            //如果是在已有药单上添加药单项，判断添加的类型是否符合
            //先检查填写的药单编号是否存在
            List<MedicationList> lists = list(new QueryWrapper<MedicationList>().eq("list_num", medicationList.getListNum()));
            if(lists.size()==0){
                return Result.error(Constants.CODE_400, "药单不存在");
            }
            //获取已存在药单的类型
            boolean existTerm = lists.get(0).getTerm();
            if (existTerm != term) {
                String msg = "无法在" + (existTerm ? "长期" : "短期") + "药单中添加" + (existTerm ? "短期" : "长期") + "药单项";
                return Result.error(Constants.CODE_400, msg);
            }
        }

        if (medicationList.getEndTime() != null && medicationList.getStartTime() != null) {
            medicationList.setTerm(term);
        } else {
            throw new ServiceException(Constants.CODE_400, "未填写药品服用时间段");
        }
        if (medicationList.getMedicationId() == null) {
            throw new ServiceException(Constants.CODE_400, "未填写药品信息");
        }
        if (medicationList.getFrequencyId() == null) {
            throw new ServiceException(Constants.CODE_400, "未填写频率信息");
        }

        //生成药单编号
        Long listNum = IdWorker.getId(medicationList);
        if (medicationList.getListNum() == null) {
            medicationList.setListNum(listNum);
        }
        //保存
        save(medicationList);
        return Result.success(listNum);
    }

    @Override
    public Result updateOption(MedicationList medicationList) {
        //判空
        MedicationList one = getById(medicationList.getId());
        if (one == null) {
            throw new ServiceException(Constants.CODE_400, "药单项ID不存在");
        }

        //根据药品的服用时间判断为长期还是临时
        long timeDifference = medicationList.getEndTime().getTime() - medicationList.getStartTime().getTime();
        boolean t = timeDifference >= (24 * 60 * 60 * 1000 * 5);
        medicationList.setTerm(t);

        /*
            进行判断：如果更新后药单项的时效与药单中其他项不符，则报错；
            如果药单中没有除此项以外的药单项，那么查找该老人的另一个药单项，获取另一个药单项的时效
            如果另一个药单的时效与此药单项不同，那么正常更新；
            如果另一个药单的时效与此药单项相同，则报错
            如果该老人不存在另一个药单，那么正常更新
         */

        //获取同药单里的其他项
        QueryWrapper<MedicationList> qw = new QueryWrapper<>();
        qw.ne("id", medicationList.getId());
        qw.eq("list_num", medicationList.getListNum());
        List<MedicationList> list = list(qw);

        if (list.size() != 0) {
            //如果有其他项，获取时效，与当前项作对比
            boolean term = list.get(0).getTerm();
            if (term != medicationList.getTerm()) {
                String msg = "无法在" + (term ? "长期" : "短期") + "药单中添加" + (term ? "短期" : "长期") + "药单项";
                return Result.error(Constants.CODE_400, msg);
            }
        } else {
            //如果无其他项，获取老人的另一个药单
            QueryWrapper<MedicationList> qw2 = new QueryWrapper<>();
            qw2.eq("elderly_id", medicationList.getElderlyId());
            qw2.ne("list_num", medicationList.getListNum());
            List<MedicationList> otherList = list(qw2);
            if (otherList.size() != 0) {
                //如果存在另一个药单，获取另一个药单的时效并进行对比
                boolean term = otherList.get(0).getTerm();
                if (term == medicationList.getTerm()) {
                    String msg = "无法在" + (!term ? "长期" : "短期") + "药单中添加" + (!term ? "短期" : "长期") + "药单项";
                    return Result.error(Constants.CODE_400, msg);
                }
            }
        }

        //更新
        updateById(medicationList);
        return Result.success();
    }

    @Override
    public Result delete(Long id) {
        //判空
        MedicationList one = getById(id);
        if (one == null) {
            throw new ServiceException(Constants.CODE_400, "不存在此ID的药单项");
        }
        //删除
        removeById(id);
        //删除用药记录
        List<MedicationList> list = new ArrayList<>();
        list.add(one);
        deleteRecordsByMList(list);
        return Result.success();
    }

    @Override
    public Result deleteBatch(Long listNum) {
        //判空
        QueryWrapper<MedicationList> qw = new QueryWrapper<MedicationList>().eq("list_num", listNum);
        List<MedicationList> list = list(qw);
        if (list.size() == 0) {
            throw new ServiceException(Constants.CODE_400, "不存在此药单编号的药单项");
        }
        removeBatchByIds(list.stream().map(MedicationList::getId).collect(Collectors.toList()));
        //删除用药记录
        deleteRecordsByMList(list);
        return Result.success();
    }

    @Override
    public Result deleteByElder(Long elderId) {
        //老人信息判空
        ElderlyInformation elder = elderlyInformationMapper.selectById(elderId);
        if (elder == null) {
            return Result.error(Constants.CODE_400, "老人信息不存在");
        }
        //获取要删除的药单项列表
        List<MedicationList> delList = list(new QueryWrapper<MedicationList>().eq("elderly_id", elderId));
        //根据它们的id删除
        removeBatchByIds(delList.stream().map(MedicationList::getId).collect(Collectors.toList()));
        //删除用药记录
        deleteRecordsByMList(delList);
        return Result.success();
    }

    /**
     * 根据药单项对象删除对应的药单记录
     */
    public void deleteRecordsByMList(List<MedicationList> list){
        for (MedicationList m : list) {
            medicationRecordMapper.delete(new QueryWrapper<MedicationRecord>().eq("medication_list_id", m.getId()));
        }
    }

}
